package com.naiterui.ehp.bs.patient.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.map.MapUtil;
import com.alibaba.fastjson.JSONObject;
import com.naiterui.common.repository.page.PageParamVO;
import com.naiterui.common.repository.page.PageVO;
import com.naiterui.ehp.bp.bo.form.DesignFormBO;
import com.naiterui.ehp.bp.bo.im.MsgSendParamBO;
import com.naiterui.ehp.bp.bo.patient.MsgMaPublishParamBO;
import com.naiterui.ehp.bp.constants.ImConstants;
import com.naiterui.ehp.bp.domain.Doctor;
import com.naiterui.ehp.bp.domain.DrFollowUpRecord;
import com.naiterui.ehp.bp.domain.DrFollowUpRecordForm;
import com.naiterui.ehp.bp.support.exception.BaseExceptionCodes;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.utils.beans.BeanUtil;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bs.doctor.service.IDrFollowUpRecordService;
import com.naiterui.ehp.bs.doctor.vo.followup.AdCalendarPointVO;
import com.naiterui.ehp.bs.doctor.vo.followup.AdVisitVO;
import com.naiterui.ehp.bs.doctor.vo.followup.DrFollowUpCheckVO;
import com.naiterui.ehp.bs.doctor.vo.followup.DrFollowUpRecordFormVO;
import com.naiterui.ehp.bs.doctor.vo.followup.DrFollowUpRecordVO;
import com.naiterui.ehp.bs.doctor.vo.followup.DrFollowUpSubVisitVO;
import com.naiterui.ehp.bs.patient.feign.DesignFormFeignClient;
import com.naiterui.ehp.bs.patient.feign.IDoctorFeignClient;
import com.naiterui.ehp.bs.patient.feign.ImFeginClient;
import com.naiterui.ehp.bs.patient.repository.IApDoctorRepository;
import com.naiterui.ehp.bs.patient.repository.IApDrFollowUpRecordFormRepository;
import com.naiterui.ehp.bs.patient.repository.IApDrFollowUpRecordRepository;
import com.naiterui.ehp.bs.patient.service.IApFollowUpRecordService;
import com.naiterui.ehp.bs.patient.service.message.IMessageSendService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author yangfuya
 * @since 2022/1/10
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ApDrFollowUpRecordServiceImpl implements IApFollowUpRecordService {
    private final IDrFollowUpRecordService followUpRecordService;
    private final IApDrFollowUpRecordRepository followUpRecordRepository;
    private final IApDrFollowUpRecordFormRepository followUpRecordFormRepository;
    private final DesignFormFeignClient designFormFeignClient;
    private final IDoctorFeignClient doctorFeignClient;
    private final IApDoctorRepository doctorRepository;
    private final IMessageSendService messageSendService;
    private final ImFeginClient imFeginClient;

    @Override
    public PageVO<DrFollowUpRecordVO> page(Long patientId, Integer status, PageParamVO pageParamVO)
            throws BusinessException {
        Page<DrFollowUpRecord> page = this.followUpRecordRepository.findByPatientIdAndStatus(patientId, status,
                pageParamVO.getPageable());
        List<DrFollowUpRecord> content = page.getContent();
        if (CollUtil.isEmpty(content)) {
            return PageVO.newBuilder().pageNo(page.getNumber()).pageSize(page.getSize())
                    .totalCount((int) page.getTotalElements()).result(Collections.EMPTY_LIST).build();
        }
        Set<Long> followUpIdSet = content.stream().map(DrFollowUpRecord::getId).collect(Collectors.toSet());
        List<DrFollowUpRecordForm> recordForms =
                this.followUpRecordFormRepository.findAllByAndFollowUpIdIn(followUpIdSet);
        Map<Long,
                List<DrFollowUpRecordFormVO>> upRecordFormMap = recordForms.stream()
                .map(r -> DrFollowUpRecordFormVO.builder().followUpId(r.getFollowUpId())
                        .followUpFormId(r.getFollowUpFormId()).followUpRecordFormId(r.getId()).formName(r.getFormName())
                        .fillingStatus(r.getFillingStatus()).build())
                .collect(Collectors.groupingBy(DrFollowUpRecordFormVO::getFollowUpId, Collectors.toList()));
        return PageVO.newBuilder().pageNo(pageParamVO.getPage()).pageSize(pageParamVO.getNum())
                .totalCount((int) page.getTotalElements())
                .result(content.stream()
                        .map(r -> DrFollowUpRecordVO.builder().id(r.getId()).name(r.getName()).doctorId(r.getDoctorId())
                                .patientId(r.getPatientId()).inquirerId(r.getInquirerId()).inquirerName(r.getInquirerName())
                                .inquirerGender(r.getInquirerGender()).inquirerAge(r.getInquirerAge()).beginTime(r.getBeginTime())
                                .status(r.getStatus()).createdAt(r.getCreatedAt())
                                .followUpFormList(upRecordFormMap.get(r.getId())).build())
                        .collect(Collectors.toList()))
                .build();
    }

    @Override
    public List<DrFollowUpRecordVO> list(Long patientId, Integer status) throws BusinessException {
        List<DrFollowUpRecord> records =
                this.followUpRecordRepository.findByPatientIdAndStatusOrderByEndTimeAsc(patientId, status);
        if (CollUtil.isEmpty(records)) {
            return Collections.emptyList();
        }
        Set<Long> followUpIdSet = records.stream().map(DrFollowUpRecord::getId).collect(Collectors.toSet());
        List<DrFollowUpRecordForm> recordForms =
                this.followUpRecordFormRepository.findAllByAndFollowUpIdIn(followUpIdSet);
        Map<Long,
                List<DrFollowUpRecordFormVO>> upRecordFormMap = recordForms.stream()
                .map(r -> DrFollowUpRecordFormVO.builder().followUpId(r.getFollowUpId())
                        .followUpFormId(r.getFollowUpFormId()).followUpRecordFormId(r.getId()).formName(r.getFormName())
                        .fillingStatus(r.getFillingStatus()).build())
                .collect(Collectors.groupingBy(DrFollowUpRecordFormVO::getFollowUpId, Collectors.toList()));

        Set<Long> doctorIdSet = records.stream().map(DrFollowUpRecord::getDoctorId).collect(Collectors.toSet());
        List<Doctor> doctorList = this.doctorRepository.findByIdIn(doctorIdSet);
        Map<Long, String> doctorMap = doctorList.stream().collect(Collectors.toMap(Doctor::getId, Doctor::getName));
        return records.stream()
                .map(r -> DrFollowUpRecordVO.builder().id(r.getId()).name(r.getName()).doctorId(r.getDoctorId())
                        .doctorName(doctorMap.get(r.getDoctorId())).patientId(r.getPatientId()).inquirerId(r.getInquirerId())
                        .inquirerName(r.getInquirerName())
                        .inquirerGender(r.getInquirerGender()).inquirerAge(r.getInquirerAge()).beginTime(r.getBeginTime())
                        .status(r.getStatus()).createdAt(r.getCreatedAt())
                        .followUpFormList(upRecordFormMap.get(r.getId())).build())
                .collect(Collectors.toList());
    }

    @Override
    public DrFollowUpRecordVO getById(Long id) throws BusinessException {
        return this.followUpRecordService.getById(id);
    }

    @Override
    public DrFollowUpRecordFormVO switchForm(Long followUpRecordFormId) throws BusinessException {
        return this.followUpRecordService.switchForm(followUpRecordFormId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DesignFormBO saveData(Long followUpRecordFormId, Map<String, Object> data) throws BusinessException {
        DrFollowUpRecordForm followUpRecordForm = followUpRecordFormRepository.findFirstById(followUpRecordFormId);
        if (followUpRecordForm == null) {
            throw new BusinessException(BaseExceptionCodes.FOLLOW_UP_RECORD_NOT_EXIST);
        }
        DrFollowUpRecord drFollowUpRecord = followUpRecordRepository.get(followUpRecordForm.getFollowUpId());
        if (drFollowUpRecord == null) {
            throw new BusinessException(BaseExceptionCodes.FOLLOW_UP_RECORD_NOT_EXIST);
        }
        if (drFollowUpRecord.getStatus() == DrFollowUpRecord.EXPIRED) {
            throw new BusinessException(BaseExceptionCodes.FOLLOW_UP_COMPLETE);
        }

        followUpRecordForm.setSubmitAt(new Date());
        followUpRecordForm.setFillingStatus(DrFollowUpRecordForm.FILLIN);
        this.followUpRecordFormRepository.update(followUpRecordForm);
        this.designFormFeignClient.saveData(followUpRecordForm.getFormId(), followUpRecordForm.getId().toString(),
                data);
        // 给医生发送填写通知
        DrFollowUpRecord followUpRecord = this.followUpRecordRepository.get(followUpRecordForm.getFollowUpId());
        MsgSendParamBO sendParamBO = new MsgSendParamBO();
        sendParamBO.setMsgType(ImConstants.MESSAGE_TYPE_FOLLOW_UP_FILLIN);
        sendParamBO.setDoctorId(followUpRecord.getDoctorId());
        sendParamBO.setPatientId(followUpRecord.getPatientId());
        sendParamBO.setInquirerId(followUpRecord.getInquirerId());
        sendParamBO.setExtId(followUpRecordFormId.toString());
        sendParamBO.setSysMsgType(ImConstants.SYS_MSG_TYPE_SYSTEM);
        sendParamBO.setLastPlatform(ImConstants.HEADER_ORIGIN_PATIENT_APP);
        sendParamBO.setRelation(ImConstants.MESSAGE_RESOURCE_PATIENT);
        sendParamBO.setVisualType(ImConstants.MESSAGE_VISUAL_TYPE_DOCTOR);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("followUpId", followUpRecordForm.getFollowUpId());
        jsonObject.put("followUpRecordFormId", followUpRecordFormId);
        jsonObject.put("content", "随访人" + followUpRecord.getInquirerName() + "已填写【" + followUpRecordForm.getFormName()
                + "】，<a style='color:#666666' href='%s'><font color='#666666'>点击查看</font></a>");
        sendParamBO.setContent(jsonObject.toJSONString());
        imFeginClient.sendSysMessage(sendParamBO);
        List<DrFollowUpRecordForm> allRecordForm = followUpRecordFormRepository.findAllByAndFollowUpId(followUpRecordForm.getFollowUpId());
        Boolean allFinish = allRecordForm.stream().allMatch(r -> r.getFillingStatus() == DrFollowUpRecordForm.FILLIN);
        if (allFinish && (followUpRecord.getStatus() == DrFollowUpRecord.NOT_STARTED || followUpRecord.getStatus() == DrFollowUpRecord.STARTED)) {
            drFollowUpRecord.setStatus(DrFollowUpRecord.STARTED);
            drFollowUpRecord.setChangedAt(new Date());
            drFollowUpRecord.setCompleteStatus(DrFollowUpRecord.COM_STATUS_YES);
            drFollowUpRecord.setCompleteTime(new Date());
            drFollowUpRecord.setOnlineType(DrFollowUpRecord.ONLINE_TYPE_DOWN);
            followUpRecordRepository.update(drFollowUpRecord);
        }
        return null;
    }

    @Override
    public DesignFormBO cacheSave(Long followUpRecordFormId, Map<String, Object> data) throws BusinessException {
        DrFollowUpRecordForm followUpRecordForm = followUpRecordFormRepository.findFirstById(followUpRecordFormId);
        if (followUpRecordForm == null) {
            throw new BusinessException(BaseExceptionCodes.FOLLOW_UP_RECORD_NOT_EXIST);
        }
        this.designFormFeignClient.saveCacheFromData(followUpRecordForm.getFormId(),
                followUpRecordForm.getId().toString(), data);
        return null;
    }

    @Override
    public Map<String, Object> cacheGet(Long followUpRecordFormId) throws BusinessException {
        DrFollowUpRecordForm followUpRecordForm = followUpRecordFormRepository.findFirstById(followUpRecordFormId);
        if (followUpRecordForm == null) {
            throw new BusinessException(BaseExceptionCodes.FOLLOW_UP_RECORD_NOT_EXIST);
        }
        return this.designFormFeignClient.getCacheFromData(followUpRecordForm.getFormId(),
                followUpRecordForm.getId().toString());
    }

    @Override
    public String upload(MultipartFile file) throws BusinessException {
        return this.designFormFeignClient.upload(file);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void followUpStarted() throws BusinessException {
        Date dateTime = cn.hutool.core.date.DateUtil.beginOfDay(new Date()).toJdkDate();
        List<DrFollowUpRecord> records = this.followUpRecordRepository.findByBeginTimeAndStatus(dateTime, DrFollowUpRecord.NOT_STARTED);
        if (CollUtil.isNotEmpty(records)) {
            records.stream().filter(r -> r.getType() == DrFollowUpRecord.TYPE_QA).forEach(d -> {
                d.setStatus(DrFollowUpRecord.STARTED);
                d.setChangedAt(new Date());
                this.followUpRecordRepository.update(d);
            });
            for (int i = 0; i < records.size(); i++) {
                DrFollowUpRecord re = records.get(i);
                // 向医生发送通知
                doctorFeignClient.sendFollowUpNotice(re.getDoctorId(), re.getId(), re.getType());
                // 向患者发送消息
                this.messageSendService.publishMaMessage(MsgMaPublishParamBO.builder().doctorId(re.getDoctorId())
                        .patientId(re.getPatientId()).msgType(MsgMaPublishParamBO.MsgType.FOLLOW_UP_STARTED)
                        .followUpId(re.getId()).content("点击可查看详情").build());
            }
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void followUpFinish() throws BusinessException {
        Date date = DateUtil.addDays(new Date(), -1);
        List<DrFollowUpRecord> records = this.followUpRecordRepository.findByTypeAndBeginTimeAndStatus(DrFollowUpRecord.TYPE_SUBSEQUENT, cn.hutool.core.date.DateUtil.beginOfDay(date).toJdkDate(), DrFollowUpRecord.NOT_STARTED);
        if (CollUtil.isNotEmpty(records)) {
            records.forEach(d -> {
                d.setStatus(DrFollowUpRecord.EXPIRED);
                d.setChangedAt(new Date());
                this.followUpRecordRepository.update(d);
            });
        }
    }

    @Override
    public DrFollowUpCheckVO checkFollowUp(Long followUpId) throws BusinessException {
        DrFollowUpRecord followUpRecord = this.followUpRecordRepository.get(followUpId);
        if (followUpRecord == null) {
            log.info("check follow up is null id: {}", followUpId);
            return DrFollowUpCheckVO.builder().followUpId(followUpId).status(DrFollowUpRecord.EXPIRED).build();
        }
        return DrFollowUpCheckVO.builder().followUpId(followUpId).status(followUpRecord.getStatus()).build();
    }

    @Override
    public PageVO<AdVisitVO> getVisitTodayPage(Long patientId, String date, PageParamVO pageParamVO) {
        Date beginDate = DateUtil.parseStrToDateTime(date + " 00:00:00");
        Page<DrFollowUpRecord> page = this.followUpRecordRepository.findByPatientIdAndBeginTime(patientId, beginDate, pageParamVO.getPageable());
        List<DrFollowUpRecord> content = page.getContent();
        if (CollUtil.isEmpty(content)) {
            return PageVO.newBuilder().pageNo(page.getNumber()).pageSize(page.getSize())
                    .totalCount((int) page.getTotalElements()).result(Collections.EMPTY_LIST).build();
        }
        List<AdVisitVO> visitListPageVo = this.getVisitListVO(content);
        return PageVO.newBuilder().pageNo(pageParamVO.getPage()).pageSize(pageParamVO.getNum())
                .totalCount((int) page.getTotalElements())
                .result(visitListPageVo)
                .build();
    }

    private List<AdVisitVO> getVisitListVO(List<DrFollowUpRecord> content) {
        Date now = new Date();
        List<AdVisitVO> result = content.stream().map(c -> {
            AdVisitVO vo = new AdVisitVO();
            BeanUtil.copyProperties(c, vo);
            vo.setComplete(DrFollowUpRecord.CANCEL != c.getStatus() && now.after(c.getEndTime()) && DrFollowUpRecord.COM_STATUS_NO == c.getCompleteStatus());
            return vo;
        }).collect(Collectors.toList());
        return result;
    }

    @Override
    public List<AdCalendarPointVO> getMonthVisit(Long patientId, String month) {
        List<AdCalendarPointVO> pointVOS = new ArrayList<>();

        DateTime beginTime = cn.hutool.core.date.DateUtil.beginOfMonth(DateUtil.parseStrToDate(month + "-01"));
        DateTime endTime = cn.hutool.core.date.DateUtil.endOfMonth(DateUtil.parseStrToDate(month + "-01"));
        List<DrFollowUpRecord> result = this.followUpRecordRepository.findByPatientIdAndBeginTimeBetween(patientId, beginTime.toJdkDate(), endTime.toJdkDate());
        if (CollUtil.isNotEmpty(result)) {
            Map<String, List<DrFollowUpRecord>> beginTimeMap = result.stream().collect(Collectors.groupingBy(r -> DateUtil.formatDate(r.getBeginTime(), DateUtil.DATE_FORMAT)));
            if (MapUtil.isNotEmpty(beginTimeMap)) {
                beginTimeMap.forEach((k, v) -> {
                    AdCalendarPointVO vo = new AdCalendarPointVO();
                    vo.setDay(k);
                    vo.setStatus(handleCalendarStatus(v));
                    pointVOS.add(vo);
                });
            }
        }
        return pointVOS;
    }

    private Integer handleCalendarStatus(List<DrFollowUpRecord> result) {
        Date now = new Date();
        Boolean expire = result.get(0).getEndTime().before(now);
        Boolean allCancel = result.stream().allMatch(r -> DrFollowUpRecord.CANCEL == r.getStatus());
        Boolean anyNotComplete = !allCancel && result.stream().anyMatch(r -> DrFollowUpRecord.COM_STATUS_NO == r.getCompleteStatus() && DrFollowUpRecord.CANCEL != r.getStatus());
        if (expire && anyNotComplete) {
            return AdCalendarPointVO.RED;
        } else if (!expire && anyNotComplete) {
            return AdCalendarPointVO.ORANGE;
        } else {
            return AdCalendarPointVO.GRAY;
        }
    }

    @Override
    public List<DrFollowUpSubVisitVO> getSubsequentVisitByFollowNo(String followNo) throws BusinessException {
        return this.followUpRecordService.getSubsequentVisitByFollowNo(followNo);
    }

}
